//
// Created by sally on 24-10-27.
//

#include "Timer_wheel.h"
#include <iostream>
#include <thread>

using namespace whq::timer;

template <int SLOT>
SingleWheel<SLOT>::SingleWheel():cur_slot(0)
{

}


template <int SLOT>
void SingleWheel<SLOT>::add(int insert_solt, std::shared_ptr<TimeTask>&& sp_task)
{
    if (insert_solt >=SLOT)
    {
        return;
    }
    task_list[insert_solt].emplace_back(std::move(sp_task));
}

template <int SLOT>
void SingleWheel<SLOT>::add_High_priority(int insert_solt, std::shared_ptr<TimeTask>&& sp_task)
{
    if (insert_solt >=SLOT)
    {
        return;
    }
    task_list[insert_solt].emplace_front(std::move(sp_task));
}

/**
 * 这个函数是时间轮推进函数，当时间指针运行到当前槽时，需要弹出当前槽的任务，并判断当前槽是否到了末尾了
 * 如果没到末尾，则时间指针继续推进，否则回到起点，因此这里返回的就是当前的任务列表和下一个时间指针是否回到了原点的标识
 * @return
 */
template <int SLOT>
std::pair<ListTaskPtr, bool> SingleWheel<SLOT>::tick()
{
    auto list = Task_pop_up(cur_slot);
    //判断下一个下一个时间指针是否运动到了末尾处
    cur_slot = cur_slot + 1 == SLOT ? 0 : cur_slot + 1;
    return std::make_pair(list,cur_slot == 0);
}

/**
 *
 * @return 这是给从时间轮使用的，移动到下一个时间槽然后取出时间槽的任务，并映射到低一级的时间轮上
 * cur_slot == 0 是判断当前时间轮的时间指针是否走完了一轮，如果走完了，则需要继续推进上一层的任务映射到下一层中，
 * 如果没有走完，不需要上一层映射了
 */
template <int SLOT>
std::pair<ListTaskPtr, bool> SingleWheel<SLOT>::degrade()
{
    //判断下一个下一个时间指针是否运动到了末尾处
    cur_slot = cur_slot + 1 == SLOT ? 0 : cur_slot + 1;
    auto list = Task_pop_up(cur_slot);

    return std::make_pair(list,cur_slot == 0);
}

/**
 *
 * @param cur_solt 当前的时间指针的位置，此指针非彼指针
 * @return 返回的是当前时间指针所在的链表
 */
template <int SLOT>
ListTaskPtr SingleWheel<SLOT>::Task_pop_up(uint8_t cur_solt)
{
    ListTaskPtr tmp;
    if (cur_solt >= SLOT)
        return tmp;
    task_list[cur_solt].swap(tmp);
    return tmp;
}

Timer_wheel& Timer_wheel::GetInstance()
{
    static Timer_wheel t;
    return t;
}

Timer_wheel::~Timer_wheel()
{
    alive = false;
}

Task_Result Timer_wheel::add_loop_timer(const std::string& name, uint32_t milliSeconds_timeout,
    std::function<bool()>&& func)
{
    return add_timer(name,milliSeconds_timeout,std::move(func),true);
}

Task_Result Timer_wheel::add_once_timer(const std::string& name, uint32_t milliseconds_timeout,
    std::function<bool()>&& func)
{
    // std::cout<<" add_once_timer"<<std::endl;
    return add_timer(name,milliseconds_timeout,std::move(func),false);
}

void Timer_wheel::delete_timer(const std::string& name)
{
    std::lock_guard<std::recursive_mutex> lock(m_mutex_);
    task_set.erase(name);
}

Timer_wheel::Timer_wheel():ahp(AsyncThreadPool::GetInstance()),runTime(std::chrono::steady_clock::now())
                           ,alive(true),First_Wheel(new SingleWheel<TVR_SIZE>)
{
    std::thread th([&]()
    {
        //需要判断计数器是否溢出了，如果溢出的话，则需要重新清除，计数的起点，因为holder，内部就是使用计数的起点，加上时间间隔来进行睡眠的
        Timer_holder holder;    //这是一个休眠类
        uint64_t times = 1;
        while (alive)
        {
            if (times ==0)
            {
                holder.reset();//重新生成计数起点
                times = 1;
            }

            //模拟时间指针推进
            tick();

            //调用睡眠
            holder.holder(uint * times++);  //没移动一个times都需要加1，因为sleep_until。
        }
    });

    th.detach();

    //生成4个从时间轮
    for (auto &item:Second_Wheel)
    {
        item = std::unique_ptr<SingleWheel<TNR_SIZE>> (new SingleWheel<TNR_SIZE>);
    }
}

void Timer_wheel::tick()
{
    //需要从当前槽中取出任务
    std::lock_guard<std::recursive_mutex> lock(m_mutex_);

    auto&& result = First_Wheel->tick();
    auto& tasks_list = result.first;
    //std::cout<<"tick函数"<<std::endl;
    // std::cout<<"tick函数1"<<std::endl;
    //遍历任务链表
    for (auto &task_prt:tasks_list )
    {

        //需要判断它是否是一直需要执行的任务
        bool is_loop = task_prt->isLoop();
        //如果它是需要一直执行的，但是它已经本定时器删除了，删除的逻辑是，在unordered_map中是保存了需要循环执行的任务的，而链表中也具有
        //指向这个任务的指针，因此共享指针不是唯一指向这个任务的指针，如果是唯一的，则说明这个任务从map中删除了，不需要一直执行了
        //循环的函数执行完毕后，还是需要重新添加到定时器中的

        if (is_loop && task_prt.unique() )
            continue;
        task_prt->execute();
        if (is_loop)
            add_timer(std::move(task_prt));
    }

    //当时间指针走完一圈回到起点之后，则从时间轮则是需要进位；每个时间轮都有一个时间指针
    bool run_a_lap = result.second;
    if (run_a_lap)
    {
        for (uint8_t i = 0;i <4;++i)
        {
            auto res = Second_Wheel[i]->degrade();
            //拿到任务链表
            auto second_tasks_list = res.first;
            //遍历任务链表，并往低级填充
            for (auto &timer_Task:second_tasks_list)
            {
                std::pair<uint8_t, uint8_t> tik = timer_Task->tick();  //这里返回的就是下一步的层级以及下一层的槽位
                if (tik.first >4)
                    continue;
                if (tik.first == 0)
                {
                    //将任务添加到主时间轮上
                    First_Wheel->add(tik.second,std::move(timer_Task));
                }

                //如果不等于0，则需要插入到下一个层级中，tik的第一个表示的是下一个需要跳的层级，但是我们使用了另一个array来存放从时间轮
                //这是从0开始的，但是我们的理论设计上，从时间轮是从1开始的，因此为了正确映射到array中，我们需要将level-1来正确映射
                else
                    Second_Wheel[tik.first-1]->add(tik.second,std::move(timer_Task));
            }
            //判断上一层是否需要继续推进呢？推进的条件是，当前层的时间轮走完了一个周期，重新变为0了，如果没有变为0则不需要推进
            //res.second 返回的是一个等不等于0的bool值，如果等于则说明需要推进，如果不等于则不需要推进
            if (!res.second)
                break;
        }
    }
}

void Timer_wheel::add_timer(std::shared_ptr<TimeTask>&& tw)
{
    //重新生成每个层级的slot，获取到时间轮的slot数组，这是根据时间轮的槽来更新当前任务需要插入的层级以及位置
    //因为时间是往前推进的，因此就需要更新，而不是插入到原先的位置
    //更新slot,这也是需要改变全局变量的，也需要加锁
    std::lock_guard<std::recursive_mutex> lock(m_mutex_);
    tw->do_loop(get_time_wheel_slot()); //这是更新时间轮的槽
    load_timer(std::move(tw));

}

Task_Result Timer_wheel::add_timer(const std::string& name, uint32_t millseconds_timeout, std::function<bool()>&& func,
    bool is_loop)
{
    //std::cout<<" add_timer"<<std::endl;
    std::lock_guard<std::recursive_mutex> lock(m_mutex_);
    //我们需要根据参数创建任务
    auto task = std::make_shared<TimeTask>(Millseconds(millseconds_timeout),std::move(func),is_loop,get_time_wheel_slot(),runTime);
    if (is_loop)
        task_set.emplace(name,task);
    //添加到任务结果中，这样可以获得任务的执行情况
    Task_Result ret = Task_Result(task);
    load_timer(std::move(task));
    return ret;
}

/**
 * 这个函数是将任务，按照任务的时间槽，放入到任务链表中
 * @param p_tt
 */
void Timer_wheel::load_timer(std::shared_ptr<TimeTask>&& p_tt)
{
    //我首先需要拿到我这个任务需要装载到的时间槽
    std::pair<uint8_t, uint8_t> result = p_tt->get_wheel_solt();
    // if (result.first == 0)
    //     std::cout<<"空的"<<std::endl;
    if (result.first >4)
    {
        return;
    }

    if (result.first == 0)
    {
        //说明我需要安装在主时间轮上
        First_Wheel->add(result.first,std::move(p_tt));
    }
    else
    {
        Second_Wheel[result.first-1]->add(result.first,std::move(p_tt));
    }
}

/**
 *
 * @return 这个函数是获得每个时间轮的当前的时间指针所在的位置
 */
std::array<uint8_t, 5> Timer_wheel::get_time_wheel_slot()
{
    std::array<uint8_t,5> slot_array ={0};
    slot_array[0] = First_Wheel->getSlot();
    // std::cout<<" SLOT: "<< static_cast<int>(First_Wheel->getSlot()) <<std::endl;
    for (uint8_t i = 0; i < 4;++i )
    {
        slot_array[i+1] = Second_Wheel[i]->getSlot();
    }

    return slot_array;
}
